package org.traffometer.traffosim.gui.view;

import java.awt.event.ActionListener;
import java.util.Observable;
import java.util.Observer;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
import javax.swing.event.DocumentListener;

import net.miginfocom.swing.MigLayout;

/**
 * The View for this application, which is a Swing JFrame containing buttons and pull-down menus
 * with which to manipulate the Stack state, and a display of the Stack contents.
 * 
 * @author George Lee
 */
@SuppressWarnings("serial")
public class View implements Observer {
  /** The JFrame containing the GUI window. */
  private JFrame frame = new JFrame("Traffosim Config");

  /** Labels for the packet parameters. */
  private JLabel batchRateLabel = new JLabel("Batch rate: ");
  private JLabel startLatLabel = new JLabel("Starting latitude: ");
  private JLabel startLngLabel = new JLabel("Starting longitude: ");
  private JLabel startTimeLabel = new JLabel("Starting time: ");

  private JLabel endLatLabel = new JLabel("Ending latitude: ");
  private JLabel endLngLabel = new JLabel("Ending longitude: ");
  private JLabel endTimeLabel = new JLabel("Ending time: ");

  private JLabel vehicleLabel = new JLabel("Vehicle name: ");

  /** Length of each text box. */
  private static final int BOX_LENGTH = 10;

  /** Text boxes for the packet parameters. 
   *  Code has been fixed so that initial values are not needed for UISpec4J
   * */
  private JTextField batchRateBox = new JTextField("", BOX_LENGTH);

  private JTextField startLatBox = new JTextField("", BOX_LENGTH);
  private JTextField startLngBox = new JTextField("", BOX_LENGTH);
  private JTextField startTimeBox = new JTextField("", BOX_LENGTH);
  private JTextField endLatBox = new JTextField("", BOX_LENGTH);
  private JTextField endLngBox = new JTextField("", BOX_LENGTH);
  private JTextField endTimeBox = new JTextField("", BOX_LENGTH);

  private JTextField vehicleBox = new JTextField("", BOX_LENGTH);

  /** The add packet button. */
  private JButton addPacketButton = new JButton("Add packet");

  /** The save configuration button. */
  private JButton saveConfigButton = new JButton("Save config");

  /** The current status of the configuration. */
  private JLabel currentStatus = new JLabel("");

  /**
   * Constructs the View. At the moment, does not actually do anything.
   */
  public View() {
    // does nothing.
  }

  /**
   * Returns the JFrame associated with this View. Used for Unit Testing.
   * 
   * @return The JFrame associated with the View.
   */
  public JFrame getJFrame() {
    return this.frame;
  }

  /**
   * Constructs and displays the GUI on the AWT event thread to ensure thread-safety.
   */
  public void showGUI() {
    // Schedule a job for the event-dispatching thread:
    // creating and showing this application's GUI.
    javax.swing.SwingUtilities.invokeLater(new Runnable() {
      public void run() {
        configureGUI();
      }
    });
  }

  /**
   * Creates the layout of the GUI window. Uses the MigLayout manager to simplify the display.
   */
  private void configureGUI() {
    JFrame.setDefaultLookAndFeelDecorated(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

    // Insert layout code here.
    MigLayout layout = new MigLayout();
    frame.setLayout(layout);

    startLatBox.setName("StartLat");
    startLngBox.setName("StartLng");
    batchRateBox.setName("BatchRate");

    startTimeBox.setName("StartTime");

    endLatBox.setName("EndLat");
    endLngBox.setName("EndLng");
    endTimeBox.setName("EndTime");
    vehicleBox.setName("VehicleName");

    frame.add(batchRateLabel);
    frame.add(batchRateBox, "wrap");

    frame.add(startLatLabel);
    frame.add(startLatBox);
    frame.add(endLatLabel);
    frame.add(endLatBox, "wrap");

    frame.add(startLngLabel);
    frame.add(startLngBox);
    frame.add(endLngLabel);
    frame.add(endLngBox, "wrap");

    frame.add(startTimeLabel);
    frame.add(startTimeBox);
    frame.add(endTimeLabel);
    frame.add(endTimeBox, "wrap");

    frame.add(vehicleLabel);
    frame.add(vehicleBox, "wrap");

    frame.add(currentStatus, "wrap, span");

    frame.add(addPacketButton);
    frame.add(saveConfigButton);

    // Show frame
    frame.pack();
    frame.setVisible(true);
  }
  
  /**
   * Enables the controller to add a listener to respond as text is entered in the batch rate field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addBatchRateDocListener(DocumentListener listener) {
    batchRateBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered in the starting
   * latitude field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addStartLatDocListener(DocumentListener listener) {
    startLatBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered into the starting
   * longitude field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addStartLngDocListener(DocumentListener listener) {
    startLngBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered into the starting time
   * field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addStartTimeDocListener(DocumentListener listener) {
    startTimeBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered into the ending
   * latitude field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addEndLatDocListener(DocumentListener listener) {
    endLatBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered into the ending
   * longitude field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addEndLngDocListener(DocumentListener listener) {
    endLngBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered into the ending time
   * field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addEndTimeDocListener(DocumentListener listener) {
    endTimeBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when text is entered into the vehicle
   * field.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addVehicleDocListener(DocumentListener listener) {
    vehicleBox.getDocument().addDocumentListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when the add packet button is pushed.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addPacketListener(ActionListener listener) {
    addPacketButton.addActionListener(listener);
  }

  /**
   * Enables the controller to add a listener to respond when the save config button is pushed.
   * 
   * @param listener The listener (supplied by the controller).
   */
  public void addSaveListener(ActionListener listener) {
    saveConfigButton.addActionListener(listener);
  }

  /**
   * The required method to implement as part of the "Observer" contract. The controller will add an
   * instance of this View as an Observer to the Model, which will result in this method being
   * invoked whenever the Model's state changes.
   * 
   * @param obs The observable instance that invoked this.
   * @param arg The value it passed.
   */
  public void update(Observable obs, Object arg) {
    this.currentStatus.setText(arg.toString());
  }
}
